<template>
	<div>
		<el-form-item label="结果描述">
			<el-input v-model="modelValue.placeholder" placeholder="请设置计算结果描述" />
		</el-form-item>
		<el-form-item label="保留小数">
			<el-input-number controls-position="right" :precision="0" :max="3" :min="0" v-model="modelValue.precision" placeholder="小数位数" />
			位
		</el-form-item>
		<el-form-item label="公式配置">
			<el-button icon="el-icon-setting" @click="showPanel">设置公式</el-button>
			<div class="calc-preview">
				<span v-for="(item, i) in modelValue.explain" :key="i">
					<div class="calc-tag" v-if="item.symbol">{{ item.name }}</div>
					<span v-else>{{ item }}</span>
				</span>
			</div>
		</el-form-item>
		<w-dialog title="配置计算公式" :width="modelValue.isPlus ? '760px' : '560px'" v-model="visible" @opened="init" @ok="validExplain">
			<template #title>
				<div>
					<span style="margin-right: 30px">计算公式配置</span>
					<el-radio-group v-model="modelValue.isPlus" @change="modeChange">
						<el-radio :label="false">简单模式</el-radio>
						<el-radio :label="true">高级模式</el-radio>
					</el-radio-group>
				</div>
			</template>
			<el-alert
				style="margin-bottom: 10px"
				v-if="showError"
				type="error"
				:description="`😢 计算公式/函数设置有误，无法完成计算，${errorInfo}`"
				:closable="false"
			/>
			<div class="calc-content" v-if="!modelValue.isPlus">
				<div class="calc-screen">
					<div>
						<span v-for="(item, i) in explainTemp" :key="i">
							<div class="calc-tag" v-if="item.symbol">{{ item.name }}</div>
							<span v-else>{{ item }}</span>
						</span>
					</div>
					<div class="calc-option">
						<el-button link icon="el-icon-back" :disabled="explainTemp.length === 0" @click="backOne">退格</el-button>
						<el-button link :disabled="explainTemp.length === 0" @click="explainTemp = []">清空</el-button>
					</div>
				</div>
				<el-form class="calc-keys">
					<el-form-item label="表单变量:">
						<div v-if="formVars.length > 0">
							<div @click="appendExplain(item)" class="calc-tag var-tag" v-for="item in formVars" :key="item.symbol">
								{{ item.name }}
							</div>
						</div>
						<div v-else style="color: #8c8c8c">表单内数值类型的字段才可作为变量参与计算</div>
					</el-form-item>
					<el-form-item label="运算符号:">
						<div>
							<div @click="appendExplain(item)" class="calc-tag" v-for="item in operators" :key="item.symbol">
								{{ item.name }}
							</div>
						</div>
					</el-form-item>
					<el-form-item label="数字键盘:">
						<div class="key-tags">
							<div @click="appendExplain(item)" class="calc-tag" v-for="item in numberKeys" :key="item">
								{{ item }}
							</div>
						</div>
					</el-form-item>
				</el-form>
			</div>
			<div v-else>
				<div>
					<span>选择公式：</span>
					<el-select v-model="checkCode" @change="(v) => (jsCodeTemp = v)">
						<el-option v-for="item in exampleCodes" :key="item.title" :label="item.title" :value="item.code" />
					</el-select>
				</div>
				<el-alert style="margin-top: 10px" type="info" description="🎈 使用js构建任意计算规则，取表单字段值（formData.字段ID）" :closable="false" />
				<div style="margin-top: 10px">
					<span>表单变量：</span>
					<template v-if="formAndTableVars.length > 0">
						<el-tooltip :content="copyOk ? '复制成功√' : '点击复制变量'" placement="top" v-for="item in formAndTableVars" :key="item.symbol">
							<div @click="copyVar(item)" class="calc-tag">{{ item.name }}</div>
						</el-tooltip>
					</template>
					<template v-else style="color: #8c8c8c">表单内数值类型的字段才可作为变量参与计算</template>
				</div>
				<code-editor ref="code" style="margin-top: 20px" v-model="jsCodeTemp" lang="javascript"></code-editor>
			</div>
		</w-dialog>
	</div>
</template>

<script>
const operators = [
	{ name: '+', symbol: '+' },
	{ name: '-', symbol: '-' },
	{ name: '×', symbol: '*' },
	{ name: '÷', symbol: '/' },
	{ name: '(', symbol: '(' },
	{ name: ')', symbol: ')' },
];

const leaveDaysCode = `function execute(formData) {
  const startStr = formData.field6552811841240
  const endStr = formData.field9424311860866

  if (!startStr || !endStr) return 0

  const startRes = /^(\d+)年(\d+)月(\d+)日\s(上|下)午$/.exec(startStr)
  const endRes = /^(\d+)年(\d+)月(\d+)日\s(上|下)午$/.exec(endStr)

  if (!startRes || !endRes) return 0

  const start = new Date(+startRes[1], +startRes[2] - 1, +startRes[3], startRes[4] === '上' ? 0 : 12)
  const end = new Date(+endRes[1], +endRes[2] - 1, +endRes[3], endRes[4] === '上' ? 12 : 24)

  const diff = end - start

  return diff > 0 ? (diff / (1000 * 60 * 60 * 24)) : 0
}`;

import CodeEditor from '@/views/workflow/components/common/CodeEditor.vue';
import ConfigMinxins from '../ConfigMinxins.js';
export default {
	name: 'CalcFormulaConfig',
	mixins: [ConfigMinxins],
	components: { CodeEditor },
	computed: {
		activeConfig() {
			return this.$store.state.selectFormItem.id;
		},
		formVars() {
			const formVars = [];
			this.getFormItem(this.$store.state.design.formItems, formVars);
			return formVars;
		},
		formAndTableVars() {
			const formVars = [];
			this.getFormItem(this.$store.state.design.formItems, formVars, true);
			return formVars;
		},
		testVars() {
			return this.formVars.map((v) => v.symbol);
		},
	},
	mounted() {
		this.init();
	},
	data() {
		return {
			copyOk: false,
			visible: false,
			showError: false,
			errorInfo: '',
			explain: '',
			explainTemp: [],
			jsCodeTemp: '',
			operators,
			numberKeys: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, '.'],
			exampleCodes: [{ title: '请假天数计算', code: leaveDaysCode }],
			checkCode: null,
			defaultJs:
				'/**\n' +
				' * @param formData 表单数据对象\n' +
				' * @returns {number} 计算结果数值\n' +
				' */\n' +
				'function execute(formData){\n' +
				'  //do something\n' +
				'  return 0\n' +
				'}',
		};
	},
	methods: {
		handleCopy() {
			if (navigator.clipboard) {
				navigator.clipboard.writeText(leaveDaysCode);
				this.$message.success('复制成功');
			}
		},
		init() {
			this.explainTemp = [...this.modelValue.explain];
			this.jsCodeTemp = this.modelValue.jsCode;
			this.$nextTick(() => {
				if (this.modelValue.isPlus && this.$refs.code) {
					this.$refs.code.reloadCode(this.jsCodeTemp);
				}
			});
		},
		showPanel() {
			this.visible = true;
		},
		backOne() {
			this.explainTemp.splice(this.explainTemp.length - 1, 1);
		},
		appendExplain(item) {
			this.explainTemp.push(item);
			//this.decodeExplain()
		},
		copyVar(item) {
			const content = document.createElement('textarea');
			// 防止手机上弹出软键盘
			content.setAttribute('readonly', 'readonly');
			content.value = item.symbol;
			document.body.appendChild(content);
			content.select();
			document.execCommand('copy');
			document.body.removeChild(content);
			this.copyOk = true;
			setTimeout(() => (this.copyOk = false), 500);
		},
		modeChange() {
			this.explainTemp = [];
			this.errorInfo = '';
			this.showError = false;
			if (this.modelValue.isPlus) {
				this.jsCodeTemp = this.modelValue.jsCode.trim().length > 0 ? this.modelValue.jsCode : this.defaultJs;
				this.$nextTick(() => {
					this.$refs.code.reloadCode(this.jsCodeTemp);
				});
			} else {
				this.jsCodeTemp = '';
				this.explainTemp = [...this.modelValue.explain];
			}
		},
		decodeExplain() {
			this.explain = '';
			this.explainTemp.forEach((item) => {
				const v = String(item.symbol || item);
				if (/[0-9.]$/.test(v)) {
					this.explain += v;
				} else {
					this.explain += v + ' ';
				}
			});
		},
		isNumberField(item) {
			return item.name === 'AmountInput' || item.name === 'NumberInput' || item.name === 'CalcFormula';
		},
		getFormItem(items, arr, tb = false) {
			items.forEach((item) => {
				//过滤掉自身组件，防止用户使用造成死循环计算
				if (item.id !== this.activeConfig) {
					if (item.name === 'SpanLayout') {
						this.getFormItem(item.props.items, arr);
					} else if (this.isNumberField(item)) {
						arr.push({
							name: item.title,
							symbol: 'formData.' + item.id,
						});
					} else if (item.name === 'TableList' && tb) {
						item.props.columns.forEach((col) => {
							if (this.isNumberField(col)) {
								arr.push({
									name: `${item.title}.${col.title}`,
									symbol: `formData.${item.id}[?].${col.id}`,
								});
							}
						});
					}
				}
			});
		},
		validExplain() {
			this.errorInfo = '';
			const formData = {};
			this.testVars.forEach((key) => (formData[key.split('.')[1]] = 0));
			let execute = null;
			try {
				if (this.modelValue.isPlus) {
					const js = this.jsCodeTemp.replace(/formData.field[0-9]+\[[\d+?]+\]/gi, (mc) => {
						return '{}';
					});
					execute = new Function('formData', `${js} return execute(formData)`);
					execute(formData);
					this.modelValue.jsCode = this.jsCodeTemp;
					this.modelValue.explain = [];
				} else {
					this.decodeExplain();
					execute = new Function('formData', `return ${this.explain}`);
					execute(formData);
					this.modelValue.explain = this.explainTemp;
					this.modelValue.jsCode = '';
				}
				console.log('test result: ', execute(formData));
				this.showError = false;
				this.visible = false;
			} catch (e) {
				console.log(e);
				this.showError = true;
				this.errorInfo = e;
			}
		},
	},
	emits: ['update:modelValue'],
	watch: {
		activeConfig() {
			this.init();
		},
	},
};
</script>

<style lang="less" scoped>
.calc-screen {
	position: relative;
	padding: 7px;
	box-sizing: unset;
	margin-bottom: 10px;
	background: #f3f3f3;
	border-radius: 5px;
	min-height: 100px;

	:deep(.calc-option) {
		position: absolute;
		bottom: 0;
		right: 10px;

		.el-button--text:last-child {
			color: @theme-danger;
		}
	}
}

.calc-preview {
	.calc-tag {
		padding: 2px;
		box-sizing: unset;
		height: 20px;
		margin: 0;
		line-height: 20px;
		border: none;
	}
}

.calc-keys {
	& > div {
		margin: 15px 0;
	}

	.el-form-item__content {
		.var-tag {
			width: auto !important;
		}

		.calc-tag {
			width: 25px;
			height: 22px;
			line-height: 22px;
			text-align: center;
		}

		.key-tags {
			width: 150px;
		}
	}
}

.calc-tag {
	cursor: pointer;
	padding: 2px 5px;
	box-sizing: unset;
	border-radius: 3px;
	margin: 0 5px;
	display: inline-block;
	border: 1px solid #eaeaea;

	&:hover {
		background: #eaeaea;
	}
}
</style>
